/*
   Copyright 2011 Jason Drake (jadrake75@gmail.com)
 
   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at
 
       http://www.apache.org/licenses/LICENSE-2.0
 
   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.
 */
package org.javad.components;

import java.awt.Color;
import java.awt.Component;
import java.awt.Desktop;
import java.awt.Image;
import java.awt.Rectangle;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.InputStream;
import java.lang.reflect.Method;
import java.util.Properties;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.swing.Icon;
import javax.swing.ImageIcon;
import javax.swing.JDialog;
import javax.swing.SwingUtilities;

public class UIHelper {

	public static Color Dark_Grey = new Color(150,150,150);
	public static Color Medium_Grey = new Color(240, 240, 248);
	public static Color Select_Grey = new Color(175,175,185);
	public static final int MEGA_BYTES = 1048576;
	
	private static Boolean INTERNAL = null;
	private static Boolean BROWSING_SUPPORTED = null;
	private static Properties SETTINGS = null;

	private static long INITIAL_MEMORY = 50l;
	
	private static Logger logger = Logger.getLogger(UIHelper.class.getName());
	
	public static void center(Component parent, JDialog dialog) {
        Rectangle r = parent.getBounds();
        if( r.width < dialog.getSize().width || r.height < dialog.getSize().height ) {
        	dialog.setLocation(5, 5);
        } else {
        	int x = r.x + (r.width - dialog.getSize().width)/2;
        	int y = r.y + (r.height - dialog.getSize().height)/2;
        	dialog.setLocation(x, y);
        }
    }
	
	/**
	 * Whether the current operating system is apple.
	 * 
	 * @return
	 */
	public static boolean isAppleOS() {
		String osName = System.getProperty("os.name").toLowerCase();
		boolean isMacOs = osName.startsWith("mac os x");
		if( logger.isLoggable(Level.FINE)) {
			logger.fine("Operating System Mac OS? " + isMacOs);
		}
		return isMacOs;
	}
	
	/** 
	 * Whether the current operating system is Windows 8.
	 * 
	 * @return
	 */
	public static boolean isWindows8() {
		String osName = System.getProperty("os.name").toLowerCase();
		boolean win8 = osName.equals("windows 8");
		if( logger.isLoggable(Level.FINE)) {
			logger.fine("Operating System Windows 8? " + win8 );
		}
		return win8;
	}
	
	public static Icon getScaledIcon(BufferedImage image, int size, int padding) {
			int w = image.getWidth();
			int h = image.getHeight();
			float ratio = (w * 1.0f)/h;
			ratio = ( ratio < 1.0f ) ? (h * 1.0f)/ (size - 2 * padding) : (w * 1.0f) /  (size - 2 * padding);
			w = Float.valueOf(w / ratio).intValue();
			h = Float.valueOf(h / ratio).intValue();
			ImageIcon icon = new ImageIcon( image.getScaledInstance(w,h,Image.SCALE_FAST));
			image.flush();
			return icon;
	}
	
	public static String convertToMultiline(String orig) { 
	    return "<html>" + orig.replaceAll("\n", "<br>").replace("\t", "&nbsp; &nbsp; ") + "</html>"; 
	} 
	
	public static boolean isDevelopmentMode() {
		if( INTERNAL != null ) {
			return INTERNAL.booleanValue();
		}
		INTERNAL = ( System.getenv("debug") != null ) ? Boolean.TRUE: Boolean.FALSE;
		try {
			File f = new File(System.getProperty("user.home") + File.separatorChar + "stampdev.properties");
			INTERNAL = Boolean.valueOf( f.exists());
		} catch( Error e ) {
			e.printStackTrace();
		}
		return INTERNAL.booleanValue();
	}
	
	/**
	 * Whether or not the system has a browser configured to open links
	 * 
	 * @return
	 */
	public static boolean isBrowsingSupported() { 
		if( BROWSING_SUPPORTED != null ) {
			return BROWSING_SUPPORTED.booleanValue();
		}
		boolean result = false;
	    if (Desktop.isDesktopSupported()) { 
	        Desktop desktop = Desktop.getDesktop(); 
	        if (desktop.isSupported(Desktop.Action.BROWSE)) { 
	        	result = true;
	        }
	    } 
	    BROWSING_SUPPORTED = Boolean.valueOf(result);
	    return result; 
	}
	
	
	public static Properties getSettings() {
		if( SETTINGS != null ) {
			return SETTINGS;
		}
		SETTINGS = new Properties();
		try {
			InputStream fis = UIHelper.class.getResourceAsStream("/settings.xml");
			SETTINGS.loadFromXML(fis);
		} catch( Exception e ) {
			logger.log(Level.SEVERE,"Error loading properties", e);
		}
	    return SETTINGS;
	}

	public static void setInitialMemory() {
		long total = Runtime.getRuntime().totalMemory() / MEGA_BYTES; // alloc
		long free = Runtime.getRuntime().freeMemory() / MEGA_BYTES; // free 
		long memory = total - free;
		INITIAL_MEMORY = memory;
		logger.info("Initializing memory consumed: " + INITIAL_MEMORY  + "MB");
	}
	
	public static long getUsedMemory( ) {
		long total = Runtime.getRuntime().totalMemory() / MEGA_BYTES; // alloc
		long free = Runtime.getRuntime().freeMemory() / MEGA_BYTES; // free 
		return total - free;
	}
	
	/**
	 * Determine the ratio of the memory for which is free to do image processing.
	 * The ratio will be equal to the free memory / memory roughly used by the image.
	 * The image memory can not be precisely determined at this time, so it leveraged
	 * as the used memory - <code>INITIAL_MEMORY</code> MB.
	 */
	public static float getAvailableMemoryRatio() {
		long total = Runtime.getRuntime().totalMemory() / MEGA_BYTES; // alloc
		long free = Runtime.getRuntime().freeMemory() / MEGA_BYTES; // free 
		long max = Runtime.getRuntime().maxMemory() / MEGA_BYTES; // max heap
		
		long image_used = Math.max( (total - free) - INITIAL_MEMORY, 0 );
		long avail = max - (total-free);
		return ( image_used > 0 ? avail / image_used : 100.0f);
	}
	

	/**
	 * Propagate a mouse event to all appropriate listeners of the parent.
	 * 
	 * @param e
	 * @param methodName
	 */
	public static void propagateMouseEvent(MouseEvent e, String methodName) {
		Method method = null;
		try {
			method = ("mouseMoved".equals(methodName) || "mouseDragged".equals(methodName)) ? 
					MouseMotionListener.class.getMethod(methodName,MouseEvent.class) : 
					MouseListener.class.getMethod(methodName, MouseEvent.class);
			Component source = (Component)e.getSource();
			Component parent = null;
			if( source.getParent() != null ) {
				parent = (Component) source.getParent();
			}
			if( parent != null && parent.getMouseListeners() != null ) {
				MouseEvent e2 = SwingUtilities.convertMouseEvent(source, e, parent);
				for(MouseListener listener : parent.getMouseListeners()) {
					try {
						method.invoke(listener, e2);
					}
					catch( Exception ex ) {
						logger.log(Level.INFO, "Exception from move event", ex);
					}
				}
			}
		} catch( NoSuchMethodException nsme ) {
			logger.log(Level.INFO, "mouseMoved or mouseDragged not found.", nsme);
		}
	}
}

